package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.auth.utils.UserContext;
import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.trade.constants.PayConstants;
import com.leyou.trade.dto.OrderDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.CartService;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ORDER_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.EVICT_ORDER_KEY;

@Slf4j
@Service
@Transactional
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private OrderLogisticsService orderLogisticsService;
    @Autowired
    private ItemClient itemClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private CartService cartService;
    @Autowired
    private PayHelper payHelper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AmqpTemplate amqpTemplate;

    @Override
    public Long createOrder(OrderFormDTO orderDto) {
        //1、订单数据
        Order order = new Order();
        //1.1 用户id
        Long userId = UserContext.getUser().getId();
        order.setUserId(userId);
        //1.2总金额
        Map<Long, Integer> carts = orderDto.getCarts();
        List<Long> ids = new ArrayList<>(carts.keySet());
        List<SkuDTO> skuDTOS = itemClient.querySkuByIds(ids);
        long total = 0;
        for (SkuDTO sku : skuDTOS) {
            Integer num = carts.get(sku.getId());
            total += sku.getPrice()*num;
        }
        order.setTotalFee(total);
        //1.3付款类型
        order.setPaymentType(orderDto.getPaymentType());
        //1.4邮费
        order.setPostFee(0L);
        //1.5实际付款
        order.setActualFee(total+order.getPostFee());
        //1.6订单状态初始化
        order.setStatus(OrderStatus.INIT);
        boolean success = save(order);
        if (!success){
            throw new LyException(500, "订单创建失败！");
        }
        // 2.写OrderDetail
        // 2.1.定义一个OrderDetail的集合
        List<OrderDetail> details = new ArrayList<>();
        // 2.2.遍历sku集合，转为detail
        for (SkuDTO sku : skuDTOS) {
            // 2.2.1.商品数量
            int num = carts.get(sku.getId());
            // 2.2.2.组装OrderDetail
            OrderDetail detail = new OrderDetail();
            detail.setOrderId(order.getOrderId());
            detail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            detail.setNum(num);
            detail.setSkuId(sku.getId());
            detail.setSpec(sku.getSpecialSpec());
            detail.setPrice(sku.getPrice());
            detail.setTitle(sku.getTitle());
            // 2.2.3.装入detail集合
            details.add(detail);
        }
        // 2.3.批量新增
        success = orderDetailService.saveBatch(details);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        // 3.写orderLogistics
        // 3.1.查询收货地址
        AddressDTO address = userClient.queryAddressById(orderDto.getAddressId());
        // 3.2.填写物流信息
        OrderLogistics logistics = address.toEntity(OrderLogistics.class);
        logistics.setOrderId(order.getOrderId());
        // 3.3.写入数据库
        success = orderLogisticsService.save(logistics);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        // 4.减库存
        try {
            itemClient.deductStock(carts);
        } catch (FeignException e) {
            throw new LyException(e.status(), e.contentUTF8());
        }
        Long orderId = order.getOrderId();
        //删除购物车中的sku
        deleteCartSkuBuOrderId(orderId);
        //发送订单编号到mq中
        amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME,EVICT_ORDER_KEY,orderId);
        // 5.返回订单编号
        return orderId;
    }

    @Override
    public OrderDTO queryOrderById(Long orderId) {
        Order order = getById(orderId);
        return new OrderDTO(order);
    }

    @Override
    public String getPayUrl(Long orderId) {
        //根据id查询订单
        Order order = getById(orderId);
        if (order==null){
            throw new LyException(400,"订单编号错误，订单不存在！");
        }
        //判断订单状态，是否为未支付
        if (order.getStatus()!=OrderStatus.INIT){
            throw new LyException(400,"订单已支付或者关闭");
        }
        //从redis中获取支付url
        String url = redisTemplate.opsForValue().get(PayConstants.PAY_URL_KEY+":"+orderId);
        if (url==null){
            //获取订单金额
            Long actualFee = order.getActualFee();
            //统一下单，获取支付链接
            /*用于测试这里的actualFee（金额）为0.01元*/
            url = payHelper.getPayUrl(orderId, 1L/*actualFee*/, PayConstants.ORDER_DESC);
            //把支付的url缓存在redis中，2小时有效期
            redisTemplate.opsForValue().set(PayConstants.PAY_URL_KEY+":"+orderId,url,2, TimeUnit.HOURS);
        }
        return url;
    }

    @Override
    public void handleWxNotify(Map<String, String> data) {
        //业务校验
        payHelper.checkReturnCode(data);
        //签名校验
        payHelper.checkResponseSignature(data);
        // 订单状态校验（保证幂等，防止重复通知）
        //订单编号
        String outTradeNo = data.get(PayConstants.ORDER_NO_KEY);
        //金额
        String totalFee = data.get(PayConstants.TOTAL_FEE_KEY);
        if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)) {
            // 数据有误
            throw new RuntimeException("响应数据有误，订单金额或编号为空！");
        }
        Long orderId = Long.valueOf(outTradeNo);
        Order order = getById(orderId);
        //订单状态校验
        if (!order.getStatus().equals(OrderStatus.INIT)) {
            // 说明订单已经支付过了，属于重复通知，直接返回
            return;
        }
        //金额校验
        Long total = Long.valueOf(totalFee);
        /*用于测试这里的actualFee（金额）为0.01元*/
        if (!total.equals(1L/*order.getActualFee()*/)) {
            throw new RuntimeException("订单金额有误，我要报警了！");
        }
        //修改订单状态
        update()
                .set("status",OrderStatus.PAY_UP.getValue())
                .set("pay_time",new Date())
                // 条件包括订单id和订单状态必须为1，乐观锁保证幂等
                .eq("order_id",orderId)
                .eq("status",OrderStatus.INIT.getValue()).update();
        log.info("处理微信支付通知成功！{}", data);
    }

    @Override
    public Integer queryOrderStatus(Long orderId) {
        Order order = getById(orderId);
        if (order==null){
            throw new LyException(400,"订单不存在");
        }
        return order.getStatus().getValue();
    }

    /**
     * 恢复库存
     */
    @Override
    public void evictOrderIfNecessary(Long orderId) {
        // 1.查询订单
        Order order = getById(orderId);
        if (order==null){//订单不存在，直接返回
            return;
        }
        //2、判断是否支付
        if (order.getStatus()!=OrderStatus.INIT){//订单已处理
            return;
        }
        //此时未支付已超时，需要关闭订单
        boolean success = update().set("status", OrderStatus.CLOSED.getValue())
                .set("close_time", new Date())
                .eq("order_id", orderId)
                // 通过乐观锁进一步保证幂等效果
                .eq("status", OrderStatus.INIT.getValue()).update();
        if (!success){//更新失败，说明别的线程抢先修改订单状态，可以不用处理
            return;
        }
        log.info("已关闭超时未支付订单：{}", orderId);
        // 4.查询OrderDetail
        List<OrderDetail> details = orderDetailService.query().eq("order_id", orderId).list();
        //5.获取sku的id和数量
        Map<Long,Integer> skuMap = new HashMap<>();
        for (OrderDetail detail : details) {
            skuMap.put(detail.getSkuId(),detail.getNum());
        }
        //6.恢复库存
        itemClient.AddStock(skuMap);
    }

    private void deleteCartSkuBuOrderId(Long orderId) {
        List<Long> skuIds = orderDetailService.listSkuIdsByOrderId(orderId);
        skuIds.forEach(id->{
            cartService.deleteCart(id);
        });
    }
}